home *** CD-ROM | disk | FTP | other *** search
-
-
- Dear C++ Programmer,
-
-
- Thank you for downloading the Loose Data Binder (LDB). The LDB is
- a generic persistent container class that is less filling (smaller
- code sizes and faster execution) and less stuffy (no towering
- convoluted hierarchies) than other conventional container class
- libraries found packaged with C++ compilers and application
- framework tools.
-
- The LDB is offered to you as shareware, meaning try before you buy.
- For other than evaluation purposes directed at reaching a buy or
- no buy decision, you are required by law to register the LDB. A
- hard copy manual will be sent to users upon receiving registration.
- The source code is also provided on either 3.5" or 5 1/4" DOS
- diskette (please specify). The source is broken down into many
- files along with a makefile for library building. PSW reserves
- the right to withdraw this offer at any time.
-
- LDB v1.4 $30 in U.S. $40 elsewhere
-
- Please make checks payable to:
-
- PSW / Power SoftWare
- P.O. Box 10072
- McLean, VA 22102 8072 USA
-
- If you have questions about the LDB you can contact me at:
-
- John Small
- Voice: (703) 759-3838
- CIS: 73757,2233
- Exec PC: MSMALL
-
- I have included tutorial chapter from the manual to get you
- started along with seven demos.
-
-
- Chapter 3
-
- Tutorial
-
-
- Copy binder.hpp, sbinder.hpp, sdata.hpp, and cbinder.hpp to your
- compiler's standard header directory and the *.cpp files to your
- source code directory if you have not already done so. Refer to
- the header file listings in the appendix for a quick reference to
- the various member functions of the LDB. If a member function's
- operation is unclear, look up its entry in the reference chapter
- for an explanation. Be sure to study the code of these examples,
- then compile (don't forget to link to binder.obj, sbinder.obj,
- sdata.obj and/or cbinder.obj as appropriate) and run them to see
- the results. Are the results what you expected? Okay let's begin.
-
-
- BDRDEM1.CPP
-
- This first example is a demo of the two Binder class constructors
- used in various configurations. Find their declarations in
- binder.hpp or the reference chapter and make a mental note of their
- parameters and defaults! I will only highlight some of their uses
- here.
-
- Okay let's walk through the code of this first example which can of
- course be found in bdrdem1.cpp. Starting in main() the first
- Binder, B1, is defined and constructed with all default parameters.
- As you can see from binder.hpp, the binder is constructed with
- flags = BDR_NO_FREE, maxNodes = BDR_MAXNODES, limit = BDR_LIMIT,
- and delta = BDR_DELTA. Don't worry about this stuff for now. All
- we have here is a very basic binder behaving as an elastic array of
- void pointers which you supply.
-
- (see bdrdem1.cpp) omitted here to reduce download size
-
- The for loop inserts the char pointers of the vector V into the
- rear of the binder treating it as if it were a queue. The loop is
- terminated when the NULL pointer of V is attempted to be inserted
- into the binder queue. The insQ() primitive always returns the
- pointer it inserts into the binder and of course it's not going to
- ever insert the NULL pointer so voiD0, the NULL void pointer is
- returned to indicate failure!
-
- Every binder can be treated as a list by internalizing the concept
- of some node being the current node. When a binder is first
- created no node is current. The stack, queue, deque, and array
- primitives don't affect the current node index, after all it is a
- list concept! Instead the primitives ins(), del(), insSort(),
- next(), and prev(), etc. are the list primitives and they do affect
- the current node pointer. The next() primitive advances the
- current index and returns the void pointer to that new current node
- if there is one. I'm treating the returned pointer from next() in
- the example as a boolean value to test when I reach the end of the
- list. One of the beauties (and pains) of C++ is that of overloaded
- operators. The implicit type cast operator voiD simply returns the
- pointer to the current node. You will soon notice that I have a
- habit of capitalizing the last letter of type names to indicate a
- pointer to that type. Here voiD means a pointer to void.
-
- Okay, enough hand holding, it's time to speed up! The next binder,
- B2, is constructed so that it is limited to a maximum of 3 nodes.
- Upon destruction of the binder all nodes will be deleted which is
- indicated by setting the flags parameter to BDR_OK_FREE. The
- default parameter is BDR_NO_FREE which mean that nodes are simply
- released when the binder is destructed. Furthermore the functions
- that attempt to delete nodes are inhibited, e.g. atFree().
- Notice the forEach() iterator and how it applies the block
- parameter to each element in the binder in sequential order. Block
- is SmallTalk terminology for a function passed as a parameter
- (C/C++ programmers say function pointers).
-
- The last binder of this example, B3, demonstrates the other
- constructor which takes a vector (array) of pointers and explodes
- them into a binder. By explode, I mean each cell of the vector
- becomes a node in the binder. Of course the original vector is
- left intact. You can implode a binder back into a dynamically
- allocated vector which is returned by the vector() primitive. The
- overloaded ++ operator simply applies the next() primitive to the
- binder. Find the definition of operator++() in binder.hpp.
-
-
- BDRDEM2.CPP
-
- In this second example, you will see several of the various stack,
- queue, list, array, and sort primitives in action. This is only a
- sampling, see the class declaration of Binder in binder.hpp for a
- complete listing of primitives. If you grasp the basic structure
- and operation of the binder here you can fill in the details from
- reading the header file.
-
- (see bdrdem2.cpp) omitted here to reduce download size
-
- With the first binder, B, of this example we see the primitive
- atIns() used. A binder's cells are indexed from 0 to n - 1 just
- like a conventional C array of n cells. AtIns() is not a list
- primitive, per say, and thus doesn't affect the binder's current
- node index. When no node is current, the current node number is
- internally set to "nodes", one position pass the last element in
- the binder. Primitives that search for a node returning its index
- will return BDR_NOTFOUND to indicate no node found. What may seem
- strange to you at first is that BDR_NOTFOUND is a large positive
- integer, the largest number of nodes that can ever be stored in a
- binder in fact. Remember that one less than this number will be
- the last element's index in a completely filled binder.
-
- The second binder, B2, is filled with a sorted list of the nodes
- from binder B. Now both B and B2 contain pointers to the same
- nodes! This could be a potentially hazardous situation but since
- both binders don't delete their nodes upon destruction (flags =
- BDR_NO_FREE in constructor) nothing is accidently deleted twice
- or even once since nothing was allocated to begin with.
-
-
- SBDRDEM1.CPP
-
- SBinder, derived from Binder and Streamable, is streamable or
- persistent in OOP's parlance. In other words, an SBinder can be
- saved on a stream and reloaded later. Streamable nodes have
- provisions for recording double ownerships and automatically
- safeguarding against accidental double deletions at the same time
- as providing streamability as we will see in this example.
-
- A class must include the STREAMABLE macro in its public section and
- be derived either publicly or privately from Streamable but never
- virtually or otherwise multiply inherited in order for it to be
- "streamable" (see StreamableInt below). If Streamable were allowed
- to be a virtual base class its pointer couldn't be type casted to
- its derived classes when reloading. All that is known at reload
- time is that it's Streamable and you get back a Streamable class
- instance pointer. The ID() member function must then be called to
- find out what type of class was reloaded. On the other hand if
- Streamable were allowed to be multiply inherited the overloaded
- stream operators couldn't decide, when storing on a stream, in
- which stream base is the valid id. You can elect not to use the
- STREAMABLE macro once you understand what is required of a class in
- a streamable hierarchy. I didn't use the STREAMABLE macro in
- declaring SBinder or CBinder but I did for SData.
-
- Be sure you remember to call ClassName::registerClass() before
- attempting to store a instance of a class onto a stream! The
- STREAMABLE macro declares two functions: store() and load() which
- you must define for your derived class.
-
- To run this example and see the results, redirect its output to a
- file and than view it. I've turned on debugging and the output
- will zip by on the screen otherwise.
-
- (see sbdrdem1.cpp) omitted here to reduce download size
-
- When an object X is referenced by more than one other objects that
- are being stored onto a stream, the first object that attempts to
- store X will succeed while the rest will automatically store only
- a link to X. When reloading X, a link to X will be automatically
- held in a holding pen inside of the StreamableClassRegistry if
- multiple attempts had been made storing X. When the additional
- objects referencing X are reloaded, the links to X are recovered.
- When the last reference to X is loaded and the link is
- reconstructed then X is released automatically from the holding
- pen. This mechanism prevents multiple copies of X from being
- stored on the stream and thus multiple copies reloaded. Before
- attempting to store a group of objects again, perhaps to another
- stream, you must call restream() for each object to reset this
- automatic linking mechanism (the SBinder automatically calls each
- node's restream()). To clear the holding pen between loads from
- the same stream or even different streams, you must call
- RestreamRegistry()!
-
- A lot of important points are made in this example, so study it
- carefully! Notice that function pointers can also be streamed when
- registered with the StreamableFncPtrRegistry! And of course the
- ID's for the classes you derive must be unique. Please note that
- 1, 2, and 3 are reserved for the SBinder, SData, and CBinder
- classes respectively.
-
- When studying this example's output notice that the binder's
- internal compare function pointer is stored on the stream and
- recovered and used to sort the reloaded binder. You can trace the
- multiple linking of the streamable integers. When the binders are
- being destructed, the nodes are unlinked and only deleted when the
- refCount is zero.
-
-
- SBDRDEM2.CPP
-
- An example of streamable strings can be found in sbdrdem2.cpp.
- Since sbdrdem2.cpp is so much like sbdrdem1.cpp except for handling
- strings instead of integers, its listing has be omitted here. You
- should take a look at it, however, to reenforce you understanding
- of deriving a class from Streamable for variant data.
-
-
- SDDEM1.CPP
-
- As you can see it takes a bit of work to make an item streamable so
- for non class objects, the SData class has been provided for your
- convenience. The next example shows how to use the SData class to
- wrap data at run time in a truly streamable package, complete with
- automatic protection against double deletion saving you the effort
- of deriving your own classes from Streamable. SData allows fully
- heterogeneous data to be bound together in the same binder.
-
- Notice in sddem1.cpp, that both integers and strings are bound
- together in the same binder! The display and sort compare
- functions are coded to act on both integers and strings! A more
- elegant approach would be to derive a class from SData that knows
- how to printOn() a stream and/or showIt() self on the console and
- perform some type of compare function.
-
- (see sddem1.cpp) omitted here to reduce download size
-
- The binders in sddem1.cpp are able to bind truly heterogeneous data
- and stream it! The test of multilinking is also a test of
- protecting against double deletion. The string "list programming!"
- isn't deleted twice in either the original binder or the binder
- loaded from the stream.
-
-
- CBINDER.CPP
-
- If the data you want bound in a streamable binder is homogeneous
- then the CBinder can improve efficiency over using SData with the
- SBinder. The CBinder can automatically handle fixed sized data
- such as structures and homogeneous variant data such as C strings
- which it defaults to, providing for both fixed and variant data the
- necessary packaging for streamability. Data can be cloned and
- copied on the fly as well but all nodes MUST be dynamically
- allocated! Keep in mind that CBinder nodes don't have the double
- ownership protection built-in that a true class derived from
- Streamable has but it doesn't require the associated coding effort
- either! It's also possible to derive new classes from CBinder for
- handling additional sorts of homogeneous variant data by overriding
- the Dstore(), Dload(), Dfree(), Dclone() and Dcopy() virtual
- functions.
-
- By the way, CBinder is derived from the SBinder which in turn was
- derived from Binder and Streamable. Studying the cbinder.cpp
- source code is another excellent way to learn about building your
- own streamable classes. If you do, you'll notice that
- CBinder::store() only has to worry about storing the additional
- fields of the CBinder, i.e. sizeofData, and relies on a call to
- SBinder::store() to store the SBinder's fields. Likewise
- CBinder::load() calls SBinder::load() to load the SBinder's fields.
- Writing your own load function is the more difficult of the two.
- Your load() should be written in such a way as to allow it to be
- called from a derived class' load(), hence the test of the InstancE
- pointer and the subsequent call of the constructor if it's NULL
- (see CBinder::load() in cbinder.cpp). You may be wondering where
- this constructor was declared; in the STREAMABLE macro of course
- (see sbinder.hpp)! It has a dummy first parameter with no default
- to insure that it is a unique, unambiguous constructor from any you
- might declare for your newly derived class. Notice too that
- initialization is left to a construct() function which can be
- called by either a regular constructor or
- YourStreamableClass::load() to initialize only the fields the
- load() is responsible for. The load function has to be a static
- member function since it is not permissible in C++ to take the
- address of a constructor and a function pointer had to be recorded
- in the StreamableClassRegistry which could be call to reconstruct
- an instance when reloading. Hence the load() of the class being
- loaded calls the default constructor defined in the STREAMABLE
- macro (how else can virtual function tables and the like be
- initialized in a port way?). The construct() is called to
- initialize only the fields of the derived class while loading the
- rest of the base class members is accomplished by calling the base
- class' load(). You may call the base's load() either before or
- after extracting data from the stream but obviously your store()
- function must have likewise stored the data in the same fashion.
-
- CBDRDEM1.CPP
-
- And now for a CBinder demo of fixed sized nodes. Remember a
- CBinder inherits all the member functions of the SBinder and that
- nodes must be dynamically allocated since they are deleted when the
- CBinder is destructed (CBinder constructor calls SBinder
- constructor with flags set to BDR_OK_FREE). Since the cloned nodes
- of the CBinder aren't instances of a class derived from Streamable,
- automatic protection against double deletion of doubly owned nodes
- can't be provided as is the case with nodes derived from
- Streamable. Moral: don't put statically allocated data into a
- CBinder via the inherited SBinder member functions. Likewise don't
- put the same node into a CBinder more than once via the SBinder
- functions. If you do, then when the CBinder goes to destruct
- itself or if you call allFree() or atFree(), etc. it's possible to
- either attempt to delete a statically allocated node or some node
- that has already been deleted - look out (crash)! You might wonder
- why the SBinder functions are left exposed (publicly inherited) -
- because you algorithm may require must nodes between CBinder such
- as with queuing network problems or other simulations.
-
- You'll note in the example that new primitives have be added by the
- CBinder class, e.g. pushCLN(), which do the same thing that their
- SBinder counterparts do only cloning or copying the data as
- required automatically but otherwise performing the same operation.
- You could do the same thing yourself without a CBinder but you
- would have to clone and/or copy everything yourself. If you are
- porting from FlexList, you'll see that the CopyBinder rounds out
- the SBinder to encompass the full "by value" operational capability
- of a FlexList.
-
- This demo shows that the CBinder provides for synthetic streamable
- nodes without your having to derive a new class from Streamable.
- You must still register the CBinder with the stream registry, and
- function pointers as usual, as well as calling restream() but you
- are saved from rewriting store() and load() functions for a
- packaging class derived from streamable.
-
- (see cbdrdem1.cpp) omitted here to reduce download size
-
-
- CBDRDEM2.CPP
-
- Be sure to look at cbdrdem2.cpp to see how the CBinder defaults to
- handling strings so you never have to write something like
- sbdrdem2.cpp yourself.
-
-
- SUMMARY
-
- As you become more familiar with the LDB family you will realize
- that you can derive your own LDB classes from Binder, SBinder,
- SData, and CBinder. For example, SBinder along with SData can be
- use as a basis for an adjacency list binder, or sparse matrix,
- tree, or graph network because of the strategic virtualizing of
- member functions such as Dfree(), Dstore(), Dload() and the back-
- link capability of the Streamable class. Note also that you can
- derive LDB's from the CBinder to handle what would otherwise be
- non-streamable classes and structures thanks to the virtualization
- of such functions as Dclone() and Dcopy() and Dfree(). The
- possibilities are enormous.
-